﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Graphics;
using Tesla.Graphics.Implementation;
using Tesla.Math;
using Tesla.Xna.Util;
using XFG = Microsoft.Xna.Framework.Graphics;
using XNA = Microsoft.Xna.Framework;

namespace Tesla.Xna.Graphics.Implementation {
    public sealed class XNASwapChainImplementation : SwapChainImplementation {
        private PresentationParameters _presentParams;
        private bool _isFullScreen;
        private IntPtr _windowHandle;
        private XNA.GraphicsDeviceManager _gdManager;
        private XFG.GraphicsDevice _graphicsDevice;
        private XFG.PresentationParameters _pp;

        /// <summary>
        /// Gets the presentation parameters the swap chain is set to.
        /// </summary>
        public override PresentationParameters PresentationParameters {
            get {
                return _presentParams;
            }
        }

        /// <summary>
        /// Gets if the swap chain is in full screen mode or not. By default, swap chains
        /// are not in full screen.
        /// </summary>
        public override bool IsFullScreen {
            get {
                return _isFullScreen;
            }
        }

        /// <summary>
        /// Gets the window handle the swap chain is bound to.
        /// </summary>
        public override IntPtr WindowHandle {
            get {
                return _windowHandle;
            }
        }

        /// <summary>
        /// Gets if the current display mode is in wide screen or not.
        /// </summary>
        public override bool IsWideScreen {
            get {
                return CurrentDisplayMode.AspectRatio >= 1.6f;
            }
        }

        /// <summary>
        /// Gets the current display mode of the swap chain.
        /// </summary>
        public override DisplayMode CurrentDisplayMode {
            get {
                XFG.DisplayMode mode = _graphicsDevice.DisplayMode;
                return new DisplayMode(mode.Width, mode.Height, XNAHelper.FromXNASurfaceFormat(mode.Format));
            }
        }

        /// <summary>
        /// Gets the handle to the monitor that contains the majority of the output.
        /// </summary>
        public override IntPtr MonitorHandle {
            get {
                return _graphicsDevice.Adapter.MonitorHandle;
            }
        }

        internal XNASwapChainImplementation(XNA.GraphicsDeviceManager gdManager, IntPtr windowHandle, PresentationParameters presentParams) {
            _gdManager = gdManager;
            _graphicsDevice = gdManager.GraphicsDevice;
            _windowHandle = windowHandle;
            _presentParams = presentParams;
            _pp = new XFG.PresentationParameters();
            _pp.DeviceWindowHandle = windowHandle;
            _pp.IsFullScreen = false;
            _pp.DisplayOrientation = XNA.DisplayOrientation.Default;
            SetXNAPresentationParameters(presentParams);
            _graphicsDevice.Reset(_pp);
        }

        ~XNASwapChainImplementation() {
            Dispose(false);
        }

        private void SetXNAPresentationParameters(PresentationParameters presentParams) {
            _pp.BackBufferFormat = XNAHelper.ToXNASurfaceFormat(presentParams.BackBufferFormat);
            _pp.BackBufferHeight = presentParams.BackBufferHeight;
            _pp.BackBufferWidth = presentParams.BackBufferWidth;
            _pp.DepthStencilFormat = XNAHelper.ToXNADepthFormat(presentParams.DepthStencilFormat);
            _pp.RenderTargetUsage = XNAHelper.ToXNARenderTargetUsage(presentParams.RenderTargetUsage);
            _pp.PresentationInterval = XNAHelper.ToXNAPresentInterval(presentParams.PresentInterval);
        }

        public override void Clear(ClearOptions options, Color color, float depth, int stencil) {
            XNA.Color c;
            XNAHelper.ConvertColor(ref color, out c);
            XFG.ClearOptions xfgClear = XFG.ClearOptions.Target;
            if((options & ClearOptions.Depth) == ClearOptions.Depth) {
                xfgClear |= XFG.ClearOptions.DepthBuffer;
            }
            if((options & ClearOptions.Stencil) == ClearOptions.Stencil) {
                xfgClear |= XFG.ClearOptions.Stencil;
            }
            _graphicsDevice.Clear(xfgClear, c, depth, stencil);
        }

        public override void GetBackBufferData<T>(Rectangle? rect, T[] data, int startIndex, int elementCount) {
            if(rect.HasValue) {
                XNA.Rectangle r;
                Rectangle temp = rect.Value;
                XNAHelper.ConvertRectangle(ref temp, out r);
                _graphicsDevice.GetBackBufferData<T>(r, data, startIndex, elementCount);
            } else {
                _graphicsDevice.GetBackBufferData<T>(null, data, startIndex, elementCount);
            }
        }

        public override void Present() {
            //TODO: Need to add checks to use the largest backbuffer, and only draw a portion of it to the window handle (refer to XNA Winforms tutorial)
            //for when we have multiple XNA swap chains.
            _graphicsDevice.Present(null, null, _windowHandle);
        }

        public override void Resize(int width, int height) {
            _presentParams.BackBufferWidth = width;
            _presentParams.BackBufferHeight = height;
            _pp.BackBufferWidth = width;
            _pp.BackBufferHeight = height;
            _graphicsDevice.Reset(_pp);
        }

        public override void Reset(IntPtr windowHandle, PresentationParameters presentParams) {
            _windowHandle = windowHandle;
            _pp.DeviceWindowHandle = _windowHandle;
            _pp.IsFullScreen = _isFullScreen;
            SetXNAPresentationParameters(presentParams);
            _graphicsDevice.Reset(_pp);
        }

        public override void ToggleFullScreen() {
            _isFullScreen = !_isFullScreen;
            _gdManager.ToggleFullScreen();
        }
    }
}
